package org.streets.eis.component.dropdown;

import org.apache.wicket.IRequestTarget;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.Page;
import org.apache.wicket.RequestCycle;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.IAjaxCallDecorator;
import org.apache.wicket.ajax.form.AjaxFormSubmitBehavior;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.behavior.AbstractBehavior;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.MarkupStream;
import org.apache.wicket.markup.html.IHeaderResponse;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.AbstractSubmitLink;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.link.AbstractLink;
import org.apache.wicket.markup.html.link.ExternalLink;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.protocol.http.WebRequest;
import org.apache.wicket.util.string.AppendingStringBuffer;
import org.apache.wicket.util.string.Strings;
import org.streets.eis.component.WebResources;
import org.streets.eis.component.model.DataModel;
import org.streets.eis.component.model.MenuItem;

public class MenuPanel extends Panel{

	private static final long serialVersionUID = 1L;

	protected static final String MENU_WICKET_ID = "menu";
	
	protected static final String MENUITEMS_WICKET_ID = "menuItems";
	
	protected static final String MENUITEM_WICKET_ID = "menuItem";
	
	protected static final String TEXT_WICKET_ID = "text";
	
	protected static final String SUBMENUS_WICKET_ID  = "subMenus";
	
	protected static final String SPLIT_MENUITEM = "-";
	
	
	private boolean isRoot = false;

	public MenuPanel(final String id, DataModel<MenuItem> model) {
		this(id, model, false);
	}

	private MenuPanel(final String id, DataModel<MenuItem> model, boolean isSubMenus) {
		super(id,Model.of(model));
		isRoot = !isSubMenus;		
		init();
	}
	
	protected void init(){
		setRenderBodyOnly(true);
		setOutputMarkupId(true);
		setVersioned(false);
		
		WebMarkupContainer container = new WebMarkupContainer(MENU_WICKET_ID) {
			private static final long serialVersionUID = 1L;
			@Override
			public final void onComponentTag(final ComponentTag tag) {
				super.onComponentTag(tag);
				if (isRoot) {
					tag.put("class", MenuPanel.this.getTheme());
				}
			}
			@Override
			public void onRender(final MarkupStream markupStream) {
				setOutputMarkupId(isRoot);
				super.onRender(markupStream);
			}
			@Override
			public String getMarkupId() {
				return MenuPanel.this.getMarkupId();
			}
		};
		add(container);

		container.add(new ListView<DataModel<MenuItem>>(MENUITEMS_WICKET_ID,getMenuModel().getChildren()) {
			private static final long serialVersionUID = 1L;
			@Override
			protected void populateItem(ListItem<DataModel<MenuItem>> item) {
				item.setRenderBodyOnly(true);
				item.add(populateMenuItem(item.getModel()));
			}
		});
		
		
		if (isRoot) {
			this.add(new AbstractBehavior() {
				private static final long serialVersionUID = 1L;
				@Override
				public void renderHead(IHeaderResponse response) {
					response.renderJavascriptReference(WebResources.Mootools.CORE_JS);
					response.renderJavascriptReference(WebResources.Mootools.FX_JS);
					response.renderJavascriptReference(WebResources.Streets.Menu.DROPDOWN_JS);
					response.renderJavascriptReference(WebResources.Mootools.WICKET_ADAPTER_JS);
						
					String id = getMarkupId();
					String initScript = "var "+id+"=new Streets.Dropdown('"+ id + "');";
					response.renderJavascript(initScript, "Menu_"+ id);
					
					renderReference(response);
				}
			});
		}
	}
	
	protected String getTheme(){
		return "dropdown";
	}

	@SuppressWarnings("unchecked")
	public DataModel<MenuItem> getMenuModel(){
		return (DataModel<MenuItem>) getDefaultModelObject();
	}
	
	protected void renderReference(IHeaderResponse response) {
		response.renderCSSReference(WebResources.Streets.Menu.MENU_CSS);
	}
	
	protected WebMarkupContainer populateMenuItem(IModel<DataModel<MenuItem>> model){ 
		DataModel<MenuItem> menuModel = model.getObject();
		if (SPLIT_MENUITEM.equals(menuModel.getObject().getDisplayText())){
			return newSplitMenuItem(model);
		} else {
			if (menuModel.getChildren().size()>0){				
				//包含下级菜单的，渲染成没有点击事件的label
				return newNoLinkMenuItem(model);				
			}else{
				String url = menuModel.getObject().getUrl();
				if (url!=null&&url.length()>0){			 
					//页面导向的菜单，渲染成externalLink
					return newExternalLinkMenuItem(model);
				} else {
					if (needFormSubmitted(menuModel.getObject())){
						//渲染成AjaxSubmitLink 自己在onMenuItemClick事件里处理
						return newAjaxSubmitLinkMenuItem(model);
					} else {
						//渲染成AjaxLink 自己在onMenuItemClick事件里处理
						return newAjaxLinkMenuItem(model);
					}
				}
			}
		}
	}	

	protected Label newMenuItemLabel(IModel<DataModel<MenuItem>> model){
		return new Label(TEXT_WICKET_ID,model){
			private static final long serialVersionUID = 1L;
			
			@SuppressWarnings("unchecked")
			public final DataModel<MenuItem> getModelObject(){
				return (DataModel<MenuItem>)getDefaultModelObject();
			}
			
			@Override
			protected void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag){
				DataModel<MenuItem> model = getModelObject();
				if (model!=null&&model.getObject()!=null){
					replaceComponentTagBody(markupStream, openTag,model.getObject().getDisplayText());
				}else{
					super.onComponentTagBody(markupStream, openTag);
				}				
			}
			
			@Override
			protected void onComponentTag(ComponentTag tag){
				super.onComponentTag(tag);
				
				DataModel<MenuItem> model = getModelObject(); 
				if (model!=null&&model.getObject()!=null){
					String className = "";
					if (model.getObject().isDisabled()){
						if (tag.getName().equalsIgnoreCase("a")) {
							tag.remove("href");
						}
						className = className + " disabled";
					}
					
					if (!Strings.isEmpty(model.getObject().getHint())) {
						tag.put("title", model.getObject().getHint());
					}
					
					if (MenuPanel.this.isRoot){
						if (model.getChildrenCount()>0){
							className = className + " hc"; //top menuitem has child
						}
					}
					
					String icon = model.getObject().getIcon();
					boolean hasIcon = icon!=null&&icon.length()>0;
					if (hasIcon){
						String contextPath = ((WebRequest)getRequest()).getHttpServletRequest().getContextPath();
						if (icon.startsWith("/")){ //web目录下的
							icon = contextPath + icon;
						}else{
							icon = contextPath + "/resources/" + icon;
						}
						tag.put("style", "background: url("+icon+") 4px center no-repeat");
						
						className = className + " hi";
					}
					
					if (!Strings.isEmpty(className)){
						tag.put("class", className);
					}
				}
			}
		};
	}
	
	protected MenuPanel newSubMenus(DataModel<MenuItem> model){ 
		MenuPanel p = new MenuPanel(SUBMENUS_WICKET_ID, model, true){
			private static final long serialVersionUID = 1L;
			@Override
			protected void onMenuItemClicked(AbstractLink link,DataModel<MenuItem> dataModel,AjaxRequestTarget target){
				MenuPanel.this.onMenuItemClicked(link, dataModel,target);
			}
		};
		p.setRenderBodyOnly(true).setVisible(model.getChildren().size() > 0);
		return p;
	}
	
	protected WebMarkupContainer newSplitMenuItem(IModel<DataModel<MenuItem>> model){ 
		WebMarkupContainer l = new WebMarkupContainer(MENUITEM_WICKET_ID){
			private static final long serialVersionUID = 1L;
			@Override
			protected void onComponentTag(ComponentTag tag){
				super.onComponentTag(tag);
				tag.put("class", "split");
			}
		};
		l.setOutputMarkupId(true);
		l.add(new Label(TEXT_WICKET_ID,"").setVisible(false));
		l.add(new WebMarkupContainer(SUBMENUS_WICKET_ID).setVisible(false));
		return l;
	}
	
	protected WebMarkupContainer newNoLinkMenuItem(IModel<DataModel<MenuItem>> model){ 
		WebMarkupContainer l = new WebMarkupContainer(MENUITEM_WICKET_ID){		
			private static final long serialVersionUID = 1L;
			@Override
			protected void onComponentTag(ComponentTag tag){
				super.onComponentTag(tag);
				tag.put("class", isRoot?"up":"left");
			}
		};
		l.add(newMenuItemLabel(model)).setOutputMarkupId(true);
		l.setEnabled(!model.getObject().getObject().isDisabled()).setOutputMarkupId(true);
		l.add(newSubMenus(model.getObject()));
		return l;
	}
	
	@SuppressWarnings("unchecked")
	protected WebMarkupContainer newExternalLinkMenuItem(IModel<DataModel<MenuItem>> model){
		MenuItem mi = model.getObject().getObject();
		String url = mi.getUrl();
		String target = "";		
		if (url.indexOf(":")>0){
			target = url.split(":")[0];
			url = url.split(":")[1];
		}
		
		if (url.endsWith("Page")){
			try {
				url = RequestCycle.get().urlFor((Class<Page>)Class.forName(url),null).toString();
			} catch (ClassNotFoundException e) {
				throw new RuntimeException("Unable to find Page Class : " + url);
			}
		} else {
			String contextPath = ((WebRequest)getRequest()).getHttpServletRequest().getContextPath();
			url = contextPath + url;
		}
		
		final String targetWindow = "window."+target;
		
		ExternalLink l = new ExternalLink(MENUITEM_WICKET_ID,url){
			private static final long serialVersionUID = 1L;
			@Override
			protected void onComponentTag(ComponentTag tag){
				super.onComponentTag(tag);

				CharSequence clickJS = tag.getString("onclick");
				if (Strings.isEmpty(clickJS)) return; 
				String strJS = clickJS.toString();
				if (!Strings.isEmpty(strJS) && strJS.indexOf(targetWindow) != 0 ){
					strJS =  targetWindow + strJS.substring("window".length());
					strJS = "if (" + targetWindow+ "){" + strJS + "}else{"+clickJS+"} return false;";
					tag.put("onclick", strJS);
				}
			}
		};
		
		l.setBeforeDisabledLink("").setAfterDisabledLink("");
		l.setEnabled(!model.getObject().getObject().isDisabled()).setOutputMarkupId(true);
		l.add(newMenuItemLabel(model));
		l.add(newSubMenus(model.getObject()));
		return l;
	}
	
	protected WebMarkupContainer newAjaxLinkMenuItem(IModel<DataModel<MenuItem>> model){		
		AjaxLink<DataModel<MenuItem>> l = new AjaxLink<DataModel<MenuItem>>(MENUITEM_WICKET_ID,model){
			private static final long serialVersionUID = 1L;
			
			@Override
			protected void onComponentTag(ComponentTag tag){
				super.onComponentTag(tag);
				tag.put("code", getModelObject().getObject().getCode());
			}
			@Override
			public void onClick(AjaxRequestTarget target) {
				MenuPanel.this.onMenuItemClicked(this,getModelObject(),target);
			}
			
			@Override
			public String getMarkupId() {
				return  getModelObject().getObject().getId();
			}
		};
		l.setBeforeDisabledLink("").setAfterDisabledLink("");
		l.setEnabled(!model.getObject().getObject().isDisabled()).setOutputMarkupId(true);
		l.add(newMenuItemLabel(model));
		l.add(newSubMenus(model.getObject()));
		return l;
	}	
	
	protected WebMarkupContainer newAjaxSubmitLinkMenuItem(final IModel<DataModel<MenuItem>> model){		
		AjaxSubmitLink l = new AjaxSubmitLink(MENUITEM_WICKET_ID){
			private static final long serialVersionUID = 1L;
			@Override
			protected void onComponentTag(ComponentTag tag){
				super.onComponentTag(tag);
				tag.put("code", model.getObject().getObject().getCode());
			}			
			@Override
			public String getMarkupId() {
				return  model.getObject().getObject().getId();
			}			
			@Override
			protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
				MenuPanel.this.onMenuItemClicked(this,model.getObject(),target);				
			}
			@Override
			protected void beforeFormSubmit(Form<?> form){
				MenuPanel.this.beforeFormSubmit(model.getObject(),form);
			}
		};
		l.setBeforeDisabledLink("").setAfterDisabledLink("");
		l.setEnabled(!model.getObject().getObject().isDisabled()).setOutputMarkupId(true);
		l.add(newMenuItemLabel(model));
		l.add(newSubMenus(model.getObject()));
		return l;
	}	
	
	private String generateMenuItemPath(DataModel<MenuItem> model){
		return MENU_WICKET_ID +  PATH_SEPARATOR + 
					MENUITEMS_WICKET_ID + PATH_SEPARATOR + 
					model.getIndexOfParent() + PATH_SEPARATOR + 
					MENUITEM_WICKET_ID+ PATH_SEPARATOR + 
					SUBMENUS_WICKET_ID;
	}
	
	private MarkupContainer findComponent(DataModel<MenuItem> model){
		DataModel<MenuItem> tmp = model.getParent();
		if (tmp==null){
			throw new RuntimeException("Unable to update the virtual Root MenuModel.");
		}
		String path = generateMenuItemPath(model);
		while(tmp.getParent()!=null){ 
			path = generateMenuItemPath(tmp) + PATH_SEPARATOR + path;
			tmp = tmp.getParent();
		}
		
		MarkupContainer p = (MarkupContainer) this.get(path);
		if (p == null || p.getParent() == null){
			throw new RuntimeException("Unable to find Component of  the MenuModel that wanted to update.");
		}
		
		return p.getParent();
	}
	
	@SuppressWarnings("unchecked")
	public void updateMenu(DataModel<MenuItem>... models){
		IRequestTarget requsetTarget = getRequestCycle().getRequestTarget();
		if (requsetTarget instanceof AjaxRequestTarget){
			AjaxRequestTarget target = (AjaxRequestTarget) requsetTarget;
			
			if (models!=null){
				for (DataModel<MenuItem> model : models){
					MarkupContainer c = findComponent(model);
					c.replaceWith(c=populateMenuItem((IModel<DataModel<MenuItem>>) c.getDefaultModel()));
					target.addComponent(c);
				}
			}
			target.appendJavascript(getMarkupId()+".refresh();");
		}
	}
	
	private abstract class AjaxSubmitLink extends AbstractSubmitLink{
		private static final long serialVersionUID = 1L;
		
		public AjaxSubmitLink(String id){
			this(id, null);
		}

		public AjaxSubmitLink(String id, final Form<?> form){
			super(id, form);

			add(new AjaxFormSubmitBehavior(form, "onclick"){
				private static final long serialVersionUID = 1L;

				@Override
				protected void onSubmit(AjaxRequestTarget target){
					AjaxSubmitLink.this.onSubmit(target, getForm());
				}

				@Override
				protected void onError(AjaxRequestTarget target){
					AjaxSubmitLink.this.onError(target, getForm());
				}

				@Override
				protected CharSequence getEventHandler(){
					return new AppendingStringBuffer(super.getEventHandler()).append("; return false;");
				}

				@Override
				protected IAjaxCallDecorator getAjaxCallDecorator(){
					return AjaxSubmitLink.this.getAjaxCallDecorator();
				}

				@Override
				protected void onComponentTag(ComponentTag tag){
					if (isLinkEnabled()){
						super.onComponentTag(tag);
					}
				}
				
				@Override
				protected void onEvent(AjaxRequestTarget target){
					AjaxSubmitLink.this.beforeFormSubmit(getForm());
					super.onEvent(target);
				}
			});

		}

		protected IAjaxCallDecorator getAjaxCallDecorator(){
			return null;
		}

		@Override
		protected void onComponentTag(ComponentTag tag){
			super.onComponentTag(tag);

			if (isLinkEnabled()){
				if (tag.getName().toLowerCase().equals("a")){
					tag.put("href", "#");
				}
			}else{
				disableLink(tag);
			}
		}

		public final void onSubmit(){
		}

		protected abstract void onSubmit(AjaxRequestTarget target, Form<?> form);
		
		protected void beforeFormSubmit(Form<?> form){			
		}

		protected void onError(AjaxRequestTarget target, Form<?> form){
		}
	}

	
	/**
	 * 只适应于需要取得页面上某component的model，而该component本身对其数据的更改未自己处理updateModel
	 * 使用时，此MenuPanel需和目标component包括在一个form当中
	 */
	protected boolean needFormSubmitted(MenuItem menuItem){
		return false;
	}
	
	/**
	 * 对于needFormSubmitted方法返回true的MenuItem，在Form提交前触发
	 * @param form
	 */
	protected void beforeFormSubmit(DataModel<MenuItem> clickedItem,Form<?> form){		
	}
	
	protected void onMenuItemClicked(AbstractLink link,DataModel<MenuItem> dataModel,AjaxRequestTarget target){		
	}

}
